﻿using System.Linq;
using CardPlay.Model.CardModel;

namespace CardPlay.Model.GameModel.Actions
{
    /// <summary>
    /// Predicate extensions for various action types.
    /// </summary>
    public static class Action
    {
        #region GameAction predicates

        public static bool IsActingOnEmptyPile(this GameAction action)
        {
            return action.CardGame.PileAt(action.Location).IsEmpty;
        }

        public static bool IsActingOnNonEmptyPile(this GameAction action)
        {
            return !action.IsActingOnEmptyPile();
        }

        public static bool IsActingOnTopCard(this GameAction action)
        {
            var card = action.CardGame.CardAt(action.Location);
            return card != null && card == action.CardGame.PileAt(action.Location).TopCard;
        }
        
        public static bool IsActingOnFaceDownCard(this GameAction action)
        {
            var card = action.CardGame.CardAt(action.Location);
            return card != null && card.Orientation == CardOrientation.FaceDown;
        }

        public static bool IsActingOnFaceUpCard(this GameAction action)
        {
            var card = action.CardGame.CardAt(action.Location);
            return card != null && card.IsFaceUp;
        }

        public static bool IsActingOnFaceDownTopCard(this GameAction action)
        {
            return action.IsActingOnTopCard() && action.IsActingOnFaceDownCard();
        }

        public static bool IsActingOnFaceUpTopCard(this GameAction action)
        {
            return action.IsActingOnTopCard() && action.IsActingOnFaceUpCard();
        }

        public static bool IsActingOnNonFoundationFaceUpTopCard(this GameAction action)
        {
            return !action.Location.Matches(CardGame.AnyFoundation) && action.IsActingOnFaceUpTopCard();
        }

        #endregion

        #region PileAction predicates

        public static bool IsActingOnEntirePile(this PileAction action)
        {
            return !action.SourcePile.IsEmpty && action.Location.Index == 0;
        }

        public static bool IsActingOnFaceUpTopPile(this PileAction action)
        {
            var pile = action.SourcePile;
            return !pile.IsEmpty && pile.Skip(action.Location.Index).All(card => card.IsFaceUp);
        }

        #endregion

        #region RemovePile predicates

        public static bool IsActingOnFaceUpTopPileInSequence(this RemovePileAction action, CardFaceSequenceSpecification sequenceSpecification)
        {
            return action.IsActingOnFaceUpTopPile() && (action.PileBeingRemoved.Count == 1 || action.PileBeingRemoved.IsInSequence(sequenceSpecification));
        }

        #endregion

        #region MovePile predicates

        public static bool IsMovingSingleCard(this MovePileAction action)
        {
            return action.PileBeingMoved.Count == 1;
        }

        public static bool IsBuildingSingleCardSameSuitAscendingRank(this MovePileAction action)
        {
            return action.IsMovingSingleCard() && action.IsBuildingSameSuitAscendingRank();
        }

        public static bool IsBuildingSingleCardSameSuitDescendingRank(this MovePileAction action)
        {
            return action.IsMovingSingleCard() && action.IsBuildingSameSuitDescendingRank();
        }

        public static bool IsBuildingSingleCardSameSuitUpOrDown(this MovePileAction action)
        {
            return action.IsMovingSingleCard() && action.IsBuilding(CardFaceSequence.SameSuitAdjacentRank);
        }

        public static bool IsBuildingAnySuitAscendingRank(this MovePileAction action)
        {
            return action.IsBuilding(CardFaceSequence.AnySuitAscendingRank, Rank.Ace);
        }

        public static bool IsBuildingSameSuitAscendingRank(this MovePileAction action)
        {
            return action.IsBuilding(CardFaceSequence.SameSuitAscendingRank, Rank.Ace);
        }

        public static bool IsBuildingSameColorAscendingRank(this MovePileAction action)
        {
            return action.IsBuilding(CardFaceSequence.SameColorAscendingRank, Rank.Ace);
        }

        public static bool IsBuildingAlternatingColorAscendingRank(this MovePileAction action)
        {
            return action.IsBuilding(CardFaceSequence.AlternatingColorAscendingRank, Rank.Ace);
        }

        public static bool IsBuildingAnySuitDescendingRank(this MovePileAction action)
        {
            return action.IsBuilding(CardFaceSequence.AnySuitDescendingRank, Rank.King);
        }

        public static bool IsBuildingSameSuitDescendingRank(this MovePileAction action)
        {
            return action.IsBuilding(CardFaceSequence.SameSuitDescendingRank, Rank.King);
        }

        public static bool IsBuildingDifferentSuitDescendingRank(this MovePileAction action)
        {
            return action.IsBuilding(CardFaceSequence.DifferentSuitDescendingRank, Rank.King);
        }

        public static bool IsBuildingAlternatingColorDescendingRank(this MovePileAction action)
        {
            return action.IsBuilding(CardFaceSequence.AlternatingColorDescendingRank, Rank.King);
        }

        public static bool IsBuildingAscendingFoundation(this MovePileAction action)
        {
            return action.IsBuildingFoundation(CardFaceSequence.SameSuitAscendingRank, Rank.Ace);
        }

        public static bool IsBuildingDescendingFoundation(this MovePileAction action)
        {
            return action.IsBuildingFoundation(CardFaceSequence.SameSuitDescendingRank, Rank.King);
        }

        public static bool IsBuildingFoundation(this MovePileAction action, CardFaceSequenceSpecification sequenceSpecification, Rank? requiredBaseRank = null)
        {
            return
                action.IsMovingSingleCard() &&
                action.TargetPile.Count < CardFace.RanksPerSuit &&
                action.IsBuilding(sequenceSpecification, requiredBaseRank);
        }

        public static bool IsBuildingTwoDeckFoundationWithIdenticalBase(this MovePileAction action)
        {
            return action.IsBuildingTwoDeckFoundation(false);
        }
        
        public static bool IsBuildingTwoDeckFoundation(this MovePileAction action, bool alternateDirection = true)
        {
            var isFirstSet = action.TargetPile.GetOrdinal() <= 4;
            var isAscendingBuild = isFirstSet || ! alternateDirection;

            // Base mustn't already exist in foundation.
            if (alternateDirection && action.TargetPile.IsEmpty && 
                action.CardGame.NonEmptyPilesNamed(StandardPileNames.RangePatternForFoundations(isFirstSet ? 1 : 5, 4)).Any(pile => pile.BottomCard.Suit == action.PileBeingMoved.BottomCard.Suit))
                return false;

            return
                action.TargetPile.Location.Matches(CardGame.AnyFoundation) &&
                action.TargetPile.Count < CardFace.RanksPerSuit &&
                isAscendingBuild
                    ? action.IsBuildingSingleCardSameSuitAscendingRank()
                    : action.IsBuildingSingleCardSameSuitDescendingRank();
        }

        public static bool IsBuilding(this MovePileAction action, CardFaceSequenceSpecification sequenceSpecification, Rank? requiredBaseRank = null)
        {
            return action.TargetPile.IsEmpty
                ? ! requiredBaseRank.HasValue || action.PileBeingMoved.BottomCard.Rank == requiredBaseRank
                : sequenceSpecification.IsSatisfiedBy(action.TargetPile.TopCard, action.PileBeingMoved.BottomCard);
        }

        #endregion
    }
}
